﻿using System;
using System.Drawing;
using CommonLibrary.Extensions;
using InputManager.Models;
using System.Collections.Generic;

namespace InputManager.Helpers
{
    public class MouseTranslatorCalculator
    {
        private const int StickCenter = 32768;
        private const int Multiplier = 256;
        private double _posx, _posy;
        private double _xVal, _yVal;
        private static LimitedStack<AxisQueue> valueQueue = new LimitedStack<AxisQueue>(10); // Que of 10 mouse possitions
        private double filter = 0.3;

        public AxisOffset CalculateValidMouseMovement(Point mousePosition, double deadZoneX, double exponentX, double multiplierX, double deadZoneY, double exponentY, double multiplierY, bool isNormalized)
        {
            _posx = Math.Sign(mousePosition.X) *
                (deadZoneX + (multiplierX * Math.Pow(Math.Abs(mousePosition.X), exponentX)));

            _xVal = _posx.Clamp(-128, 128) * Multiplier;

            _posy = Math.Sign(mousePosition.Y) *
                (deadZoneY + (multiplierY * Math.Pow(Math.Abs(mousePosition.Y), exponentY)));

            _yVal = _posy.Clamp(-128, 128) * Multiplier;

            if (isNormalized) // Should be a bool that checks if normalization is set
                return GetNormalizedValue();

            return CalculateFromCenter();
        }

        public AxisOffset ConfigMouseMovement(double angle, double offsetX, double multiplierX, double offsetY, double multiplierY)
        {
            //var posx = offsetX + (multiplierX * Math.Sin(angle));
            var sin = Math.Sin(angle);
            _posx = (Math.Sign(sin) * offsetX) + (multiplierX * sin);
            _xVal = _posx.Clamp(-128, 128) * Multiplier;
            
            //var posy = offsetY + (multiplierY * Math.Cos(angle));
            var cos = Math.Cos(angle);
            _posy = (Math.Sign(cos) * offsetY) + (multiplierY * cos);
            _yVal = _posy.Clamp(-128, 128) * Multiplier;

            return CalculateFromCenter();
        }
                
        private AxisOffset GetNormalizedValue()
        {
            int count = 1;
            double average = 1;
            double modifier;            

            foreach (var values in valueQueue)
            {
                modifier = Math.Pow(filter, count);

                _xVal += values.X * modifier;
                _yVal += values.Y * modifier;

                average += modifier;
                count++;
            }

            _xVal /= average;
            _yVal /= average;

            // add value to the data stack 
            valueQueue.Push(new AxisQueue() { X = _xVal, Y = _yVal });            

            return CalculateFromCenter();
        }

        private AxisOffset CalculateFromCenter()
        {
            var axis = new AxisOffset();
            
            //TODO loss of resolution here from double to int(ushort), could be optimized
            axis.X = (ushort)(StickCenter + _xVal.Clamp(short.MinValue, short.MaxValue));
            axis.Y = (ushort)(StickCenter + _yVal.Clamp(short.MinValue, short.MaxValue));
            
            return axis;            
        }
    }
}
